#!/usr/bin/env node

/* global process setImmediate */

import {Future, fork, race, chain, resolve as sync} from '../index.js';
import {log} from 'util';

var start = Date.now();
var batch = 0;
var stamp = Date.now();

function report(){
  var memMB = process.memoryUsage().rss / 1048576;
  var now = Date.now();
  var passed = now - stamp;
  batch = batch + 1;
  if(passed >= 5000){
    log(
      '-BATCH:', batch,
      '-OPS:', Math.round(batch / ((now - start) / passed) / (passed / 1000)),
      '-MEM:', memMB, 'MB'
    );
    stamp = now;
  }
}

function noop(){}

function async(x){
  return Future(function(l, r){ setImmediate(r, x); return noop });
}

var cases = Object.create(null);

//Should infinitely run until finally running out of memory.
cases.syncHeadRecursion = function recur(){
  report();
  return race(sync('r'))(chain(recur)(sync('l')));
};

//Should immediately exit with "l".
cases.syncDeepRecursion = function recur(){
  report();
  return race(chain(recur)(sync('r')))(sync('l'));
};

//Should infinitely run without any problems.
cases.syncTailRecursion = function recur(){
  report();
  return chain(recur)(race(sync('r'))(sync('l')));
};

//Should immediately exit with "r".
cases.asyncHeadRecursion = function recur(){
  report();
  return race(async('r'))(chain(recur)(async('l')));
};

//Should immediately exit with "l".
cases.asyncDeepRecursion = function recur(){
  report();
  return race(chain(recur)(async('r')))(async('l'));
};

//Should infinitely run without any problems.
cases.asyncTailRecursion = function recur(){
  report();
  return chain(recur)(race(async('r'))(async('l')));
};

//Expected to run out of memory.
cases.debugModeSyncTailRecursion = function(){
  Future.debugMode(true);
  return cases.syncTailRecursion();
};

//Expected to run out of memory.
cases.debugModeAsyncTailRecursion = function(){
  Future.debugMode(true);
  return cases.asyncTailRecursion();
};

var f = cases[process.argv[2]];

if(typeof f !== 'function'){
  console.log('Usage:\n\n  test-mem <case>\n\nPossible cases:\n');
  Object.keys(cases).forEach(function(k){console.log(`  ${k}`)});
  process.exit(0);
}

log('PID', process.pid);

var cancel = fork
  (function(e){console.error(e.stack); process.exit(1)})
  (function(v){log('resolved', v)})
  (f());

process.once('SIGINT', () => {
  log('SIGINT caught. Cancelling...');
  cancel();
});
